﻿#pragma once

namespace fast_io
{

namespace details
{

template <typename allocator_type, typename instmtype, typename indecotype, ::std::integral input_char_type>
inline constexpr input_char_type *
decoread_some_underflow_define_sz_impl(instmtype instm, indecotype indeco,
									   basic_io_buffer_pointers<typename instmtype::input_char_type> &input_buffer,
									   input_char_type *first, input_char_type *last, ::std::size_t sz)
{
	using typed_allocator_type =
		::fast_io::typed_generic_allocator_adapter<allocator_type, typename instmtype::input_char_type>;
	auto bufbg{input_buffer.buffer_begin};
	if (bufbg == nullptr)
	{
		input_buffer.buffer_end = input_buffer.buffer_curr = input_buffer.buffer_begin = bufbg =
			typed_allocator_type::allocate(sz);
	}
	auto bufcur{input_buffer.buffer_curr};
	auto bufed{input_buffer.buffer_end};
	for (;;)
	{
		if (bufcur != bufed)
		{
			auto [bufferit, it] = indeco.input_process_chars(bufcur, bufed, first, last);
			input_buffer.buffer_curr = bufferit - bufcur + bufcur;
			first = it;
			if (first == last)
			{
				return first;
			}
		}
		auto ret{::fast_io::operations::decay::read_some_decay(instm, bufbg, bufbg + sz)};
		input_buffer.buffer_curr = bufcur = bufbg;
		input_buffer.buffer_end = bufed = ret;
		if (ret == bufbg)
		{
			return first;
		}
	}
}

template <typename allocator_type, ::std::size_t sz, typename instmtype, typename indecotype,
		  ::std::integral input_char_type>
inline constexpr input_char_type *
decoread_some_underflow_define_impl(instmtype instm, indecotype indeco,
									basic_io_buffer_pointers<typename instmtype::input_char_type> &input_buffer,
									input_char_type *first, input_char_type *last)
{
	return ::fast_io::details::decoread_some_underflow_define_sz_impl<allocator_type>(instm, indeco, input_buffer,
																					  first, last, sz);
}

} // namespace details

template <typename io_buffer_type>
inline constexpr typename io_buffer_type::input_char_type *
read_some_underflow_define(basic_io_deco_filter_ref<io_buffer_type> filter,
						   typename io_buffer_type::input_char_type *first,
						   typename io_buffer_type::input_char_type *last)
{
	auto &idoref{*filter.idoptr};
	using traits_type = typename io_buffer_type::traits_type;
	return ::fast_io::details::decoread_some_underflow_define_impl<typename traits_type::allocator_type,
																   traits_type::input_buffer_size>(
		::fast_io::operations::input_stream_ref(idoref.handle),
		::fast_io::operations::refs::input_decorators_ref(idoref.decorators), idoref.input_buffer, first, last);
}

} // namespace fast_io
